﻿using MCP.BL;
using MCP.BL.Messages;
using MCP.Model.Helpers;
using MCP.Model.Repositories.Interfaces;
using System;
using System.Collections.Generic;
using System.Linq;
using client = MCP.Data.Client;
using server = MCP.Data.CF_Entities;

namespace MCP.Model.Repositories.CF
{
    public class CategoryRepository : ICategoryRepository
    {
        private readonly MCPContext context;

        public CategoryRepository(MCPContext context)
        {
            this.context = context;
        }

        /// <summary>
        /// Gets current user categories.
        /// </summary>
        /// <returns>Categories list.</returns>
        public OperationResult<List<client.Category>> GetUserCategories()
        {
            OperationResult<List<client.Category>> result;

            try
            {
                var categories = this.GetCategories(CurrentUserHelper.UserID)
                    .ToClientCategories()
                    .ToList();

                result = new OperationResult<List<client.Category>>(categories);
            }
            catch (Exception exc)
            {
                result = new OperationResult<List<client.Category>>(exc);
            }


            return result;
        }

        public OperationResult<List<client.Category>> GetCategoriesByNames(IEnumerable<string> clientCategories)
        {
            if (clientCategories == null) return null;
            OperationResult<List<client.Category>> result = null;

            try
            {
                var categories = this.GetCategories(clientCategories)
                    .ToClientCategories()
                    .ToList();
                result = new OperationResult<List<client.Category>>(categories);
            }
            catch (Exception exception)
            {
                result = new OperationResult<List<client.Category>>(exception);
            }

            return result;
        }

        /// <summary>
        /// Parses list of client categories (string[]) and adds categories which not exists yet.
        /// </summary>
        /// <param name="categories">Cleint categories list.</param>
        /// <returns>Operation (adding new categories to DB) result.</returns>
        public OperationResult PatchCategories(IEnumerable<string> categories)
        {
            if (categories == null) return null;

            OperationResult operationResult;

            try
            {
                // TODO: possible NullReferenceException!
                categories = categories.ToList();

                var existedCategories = this.GetCategories(categories).Select(c => c.CategoryName).ToList();
                var notExistedCategories = categories.Except(existedCategories).ToList();

                foreach (var cat in notExistedCategories)
                {
                    this.context.Categories.Add(new server.Category() { CategoryName = cat });
                }

                this.context.SaveChanges();

                operationResult = new OperationResult(
                    true,
                    //SimpleMessage.Category.GetCategoriesAddedMessage(notExistedCategories.Count));
                    new SimpleMessage(string.Format("{0} new {1} has been added.", notExistedCategories.Count, notExistedCategories.Count > 1 ? "categories" : "category"), MessagePriority.Low, MessageType.Notification));
            }
            catch (Exception exc)
            {
                operationResult = new OperationResult(exc);
            }

            return operationResult;
        }

        /// <summary>
        /// Filters those categories that exists in DB.
        /// </summary>
        /// <param name="categoriesName">Categories names to look for.</param>
        /// <returns>Collection of categories that exists in DB.</returns>
        private IEnumerable<server.Category> GetCategories(IEnumerable<string> categoriesName)
        {
            return this.context.Categories.Where(c => categoriesName.Contains(c.CategoryName));
        }

        /// <summary>
        /// Gets categories for specific user.
        /// </summary>
        /// <param name="userID">User ID.</param>
        /// <returns>List of server categories.</returns>
        private IEnumerable<server.Category> GetCategories(int userID)
        {
            return from category in this.context.Categories
                   where category.Items.Count(i => i.Check.UserID == userID) > 0
                   select category;
        }

        #region IDisposable
        // Copied from:
        // http://www.asp.net/mvc/tutorials/getting-started-with-ef-using-mvc/implementing-the-repository-and-unit-of-work-patterns-in-an-asp-net-mvc-application

        private bool disposed = false;

        protected virtual void Dispose(bool disposing)
        {
            if (!this.disposed)
            {
                if (disposing)
                {
                    context.Dispose();
                }
            }
            this.disposed = true;
        }

        public void Dispose()
        {
            Dispose(true);
            GC.SuppressFinalize(this);
        }

        #endregion
    }
}